package com.kalua.list.harvester.execution;

import static com.kalua.list.harvester.HarvesterConstants.DEFAULT_CONNECT_TIMEOUT;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.kalua.list.domainmodel.TextIdentifier;
import com.kalua.list.harvester.crawldata.CrawlEntryPoints;
import com.kalua.list.harvester.crawldata.History;
import com.kalua.list.harvester.parser.ContentLinkExtractorFactory;

public class CrawlExecutorImpl 	extends ThreadPoolExecutor  
													implements CrawlExecutor {

	private static final Logger logger = LoggerFactory.getLogger(CrawlExecutor.class);	
		
	private ContentLinkExtractorFactory contentLinkExtractorFactory;
	
	private int connectTimeout;
	
	public CrawlExecutorImpl(	ContentLinkExtractorFactory contentLinkExtractorFactory, 
												Integer corePoolSize, 
												Integer maximumPoolSize,
												Long keepAliveTime) {
		super(	corePoolSize, maximumPoolSize, keepAliveTime, 
					TimeUnit.SECONDS, 
					new LinkedBlockingQueue<>());
		this.contentLinkExtractorFactory = contentLinkExtractorFactory;
		this.connectTimeout = DEFAULT_CONNECT_TIMEOUT;
	}
	
	public ContentLinkExtractorFactory getContentLinkExtractorFactory() 
	{ return this.contentLinkExtractorFactory; }
	
	public void setConnectTimeout(int connectTimeout) 
	{ this.connectTimeout = connectTimeout; }
	public int getConnectTimeout() { return this.connectTimeout; }

	@Override
	public synchronized void crawl(TextIdentifier retailerId, 
														History history, 
														CrawlEntryPoints entryPoints){
		for (String root : entryPoints.getLinks()) {
			this.followLink(retailerId, history, root);
		}
	}
	
	@Override
	protected void afterExecute(Runnable task, Throwable failure) {
		super.afterExecute(task, failure);
		notifyOnEmptyQueue();
	}
	
	@Override
	public synchronized void waitUntilIdle() throws InterruptedException {
		while (!getQueue().isEmpty())
			wait();
	}
	
	private synchronized void notifyOnEmptyQueue() {
		if (this.getQueue().isEmpty())
			notify();
	}
	
	private void followLink(	TextIdentifier retailerId, 
											History history, 
											String toFollow) {
		if (history.registerLink(toFollow)) {		
			CrawlTask chaser = 
					new CrawlTask(	0, toFollow, this.connectTimeout, this, 
												this.contentLinkExtractorFactory.createExtractor(retailerId), 
												history);
			logger.info("Following link {} at depth {}.", toFollow, 0);
			execute(chaser);
		} else logger.debug("Ignoring link {} as it has already been visited.");
	}//end method def
}
